// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <stdint.h>

#include <memory>
#include <string>

#include "base/memory/ptr_util.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "media/audio/audio_manager.h"
#include "media/audio/audio_manager_base.h"
#include "media/audio/audio_output_dispatcher_impl.h"
#include "media/audio/audio_output_proxy.h"
#include "media/audio/audio_output_resampler.h"
#include "media/audio/fake_audio_log_factory.h"
#include "media/audio/fake_audio_output_stream.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

using media::AudioBus;
using media::AudioInputStream;
using media::AudioManager;
using media::AudioManagerBase;
using media::AudioOutputDispatcher;
using media::AudioOutputProxy;
using media::AudioOutputStream;
using media::AudioParameters;
using media::FakeAudioOutputStream;
using ::testing::_;
using ::testing::AllOf;
using ::testing::DoAll;
using ::testing::Field;
using ::testing::Mock;
using ::testing::NotNull;
using ::testing::Return;
using ::testing::SetArrayArgument;

namespace {

static const int kTestCloseDelayMs = 10;

// Delay between callbacks to AudioSourceCallback::OnMoreData.
static const int kOnMoreDataCallbackDelayMs = 10;

// Let start run long enough for many OnMoreData callbacks to occur.
static const int kStartRunTimeMs = kOnMoreDataCallbackDelayMs * 10;

class MockAudioOutputStream : public AudioOutputStream {
public:
    MockAudioOutputStream(AudioManagerBase* manager,
        const AudioParameters& params)
        : start_called_(false)
        , stop_called_(false)
        , params_(params)
        , fake_output_stream_(
              FakeAudioOutputStream::MakeFakeStream(manager, params_))
    {
    }

    void Start(AudioSourceCallback* callback)
    {
        start_called_ = true;
        fake_output_stream_->Start(callback);
    }

    void Stop()
    {
        stop_called_ = true;
        fake_output_stream_->Stop();
    }

    ~MockAudioOutputStream() { }

    bool start_called() { return start_called_; }
    bool stop_called() { return stop_called_; }

    MOCK_METHOD0(Open, bool());
    MOCK_METHOD1(SetVolume, void(double volume));
    MOCK_METHOD1(GetVolume, void(double* volume));
    MOCK_METHOD0(Close, void());

private:
    bool start_called_;
    bool stop_called_;
    AudioParameters params_;
    std::unique_ptr<AudioOutputStream> fake_output_stream_;
};

class MockAudioManager : public AudioManagerBase {
public:
    MockAudioManager()
        : AudioManagerBase(base::ThreadTaskRunnerHandle::Get(),
            base::ThreadTaskRunnerHandle::Get(),
            &fake_audio_log_factory_)
    {
    }
    ~MockAudioManager() override { Shutdown(); }

    MOCK_METHOD0(HasAudioOutputDevices, bool());
    MOCK_METHOD0(HasAudioInputDevices, bool());
    MOCK_METHOD0(GetAudioInputDeviceModel, base::string16());
    MOCK_METHOD3(MakeAudioOutputStream,
        AudioOutputStream*(const AudioParameters& params,
            const std::string& device_id,
            const LogCallback& log_callback));
    MOCK_METHOD2(MakeAudioOutputStreamProxy, AudioOutputStream*(const AudioParameters& params, const std::string& device_id));
    MOCK_METHOD3(MakeAudioInputStream,
        AudioInputStream*(const AudioParameters& params,
            const std::string& device_id,
            const LogCallback& log_callback));
    MOCK_METHOD0(ShowAudioInputSettings, void());
    MOCK_METHOD0(GetTaskRunner, scoped_refptr<base::SingleThreadTaskRunner>());
    MOCK_METHOD0(GetWorkerTaskRunner,
        scoped_refptr<base::SingleThreadTaskRunner>());
    MOCK_METHOD1(GetAudioInputDeviceNames, void(media::AudioDeviceNames* device_name));
    MOCK_METHOD0(GetName, const char*());

    MOCK_METHOD2(MakeLinearOutputStream,
        AudioOutputStream*(const AudioParameters& params,
            const LogCallback& log_callback));
    MOCK_METHOD3(MakeLowLatencyOutputStream,
        AudioOutputStream*(const AudioParameters& params,
            const std::string& device_id,
            const LogCallback& log_callback));
    MOCK_METHOD3(MakeLinearInputStream,
        AudioInputStream*(const AudioParameters& params,
            const std::string& device_id,
            const LogCallback& log_callback));
    MOCK_METHOD3(MakeLowLatencyInputStream,
        AudioInputStream*(const AudioParameters& params,
            const std::string& device_id,
            const LogCallback& log_callback));
    MOCK_METHOD2(GetPreferredOutputStreamParameters, AudioParameters(const std::string& device_id, const AudioParameters& params));

private:
    media::FakeAudioLogFactory fake_audio_log_factory_;
};

class MockAudioSourceCallback : public AudioOutputStream::AudioSourceCallback {
public:
    int OnMoreData(base::TimeDelta /* delay */,
        base::TimeTicks /* delay_timestamp */,
        int /* prior_frames_skipped */,
        AudioBus* dest) override
    {
        dest->Zero();
        return dest->frames();
    }
    MOCK_METHOD1(OnError, void(AudioOutputStream* stream));
};

} // namespace

namespace media {

class AudioOutputProxyTest : public testing::Test {
protected:
    void SetUp() override
    {
        // Use a low sample rate and large buffer size when testing otherwise the
        // FakeAudioOutputStream will keep the message loop busy indefinitely; i.e.,
        // RunUntilIdle() will never terminate.
        params_ = AudioParameters(AudioParameters::AUDIO_PCM_LINEAR,
            CHANNEL_LAYOUT_STEREO, 8000, 16, 2048);
        InitDispatcher(base::TimeDelta::FromMilliseconds(kTestCloseDelayMs));
    }

    void TearDown() override
    {
        // This is necessary to free all proxy objects that have been
        // closed by the test.
        base::RunLoop().RunUntilIdle();
    }

    virtual void InitDispatcher(base::TimeDelta close_delay)
    {
        dispatcher_impl_ = base::MakeUnique<AudioOutputDispatcherImpl>(
            &manager(), params_, std::string(), close_delay);
    }

    virtual void OnStart() { }

    MockAudioManager& manager()
    {
        return manager_;
    }

    void WaitForCloseTimer(MockAudioOutputStream* stream)
    {
        base::RunLoop run_loop;
        EXPECT_CALL(*stream, Close())
            .WillOnce(testing::InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
        run_loop.Run();
    }

    void CloseAndWaitForCloseTimer(AudioOutputProxy* proxy,
        MockAudioOutputStream* stream)
    {
        // Close the stream and verify it doesn't happen immediately.
        proxy->Close();
        Mock::VerifyAndClear(stream);

        // Wait for the actual close event to come from the close timer.
        WaitForCloseTimer(stream);
    }

    // Basic Open() and Close() test.
    void OpenAndClose(AudioOutputDispatcher* dispatcher)
    {
        MockAudioOutputStream stream(&manager_, params_);

        EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
            .WillOnce(Return(&stream));
        EXPECT_CALL(stream, Open())
            .WillOnce(Return(true));

        AudioOutputProxy* proxy = dispatcher->CreateStreamProxy();
        EXPECT_TRUE(proxy->Open());
        CloseAndWaitForCloseTimer(proxy, &stream);
    }

    // Creates a stream, and then calls Start() and Stop().
    void StartAndStop(AudioOutputDispatcher* dispatcher)
    {
        MockAudioOutputStream stream(&manager_, params_);

        EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
            .WillOnce(Return(&stream));
        EXPECT_CALL(stream, Open())
            .WillOnce(Return(true));
        EXPECT_CALL(stream, SetVolume(_))
            .Times(1);

        AudioOutputProxy* proxy = dispatcher->CreateStreamProxy();
        EXPECT_TRUE(proxy->Open());

        proxy->Start(&callback_);
        OnStart();
        proxy->Stop();

        CloseAndWaitForCloseTimer(proxy, &stream);
        EXPECT_TRUE(stream.stop_called());
        EXPECT_TRUE(stream.start_called());
    }

    // Verify that the stream is closed after Stop() is called.
    void CloseAfterStop(AudioOutputDispatcher* dispatcher)
    {
        MockAudioOutputStream stream(&manager_, params_);

        EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
            .WillOnce(Return(&stream));
        EXPECT_CALL(stream, Open())
            .WillOnce(Return(true));
        EXPECT_CALL(stream, SetVolume(_))
            .Times(1);

        AudioOutputProxy* proxy = dispatcher->CreateStreamProxy();
        EXPECT_TRUE(proxy->Open());

        proxy->Start(&callback_);
        OnStart();
        proxy->Stop();

        // Wait for the close timer to fire after StopStream().
        WaitForCloseTimer(&stream);
        proxy->Close();
        EXPECT_TRUE(stream.stop_called());
        EXPECT_TRUE(stream.start_called());
    }

    // Create two streams, but don't start them.  Only one device must be opened.
    void TwoStreams(AudioOutputDispatcher* dispatcher)
    {
        MockAudioOutputStream stream(&manager_, params_);

        EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
            .WillOnce(Return(&stream));
        EXPECT_CALL(stream, Open())
            .WillOnce(Return(true));

        AudioOutputProxy* proxy1 = dispatcher->CreateStreamProxy();
        AudioOutputProxy* proxy2 = dispatcher->CreateStreamProxy();
        EXPECT_TRUE(proxy1->Open());
        EXPECT_TRUE(proxy2->Open());
        proxy1->Close();
        CloseAndWaitForCloseTimer(proxy2, &stream);
        EXPECT_FALSE(stream.stop_called());
        EXPECT_FALSE(stream.start_called());
    }

    // Open() method failed.
    void OpenFailed(AudioOutputDispatcher* dispatcher)
    {
        MockAudioOutputStream stream(&manager_, params_);

        EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
            .WillOnce(Return(&stream));
        EXPECT_CALL(stream, Open())
            .WillOnce(Return(false));
        EXPECT_CALL(stream, Close())
            .Times(1);

        AudioOutputProxy* proxy = dispatcher->CreateStreamProxy();
        EXPECT_FALSE(proxy->Open());
        proxy->Close();
        EXPECT_FALSE(stream.stop_called());
        EXPECT_FALSE(stream.start_called());
    }

    void CreateAndWait(AudioOutputDispatcher* dispatcher)
    {
        MockAudioOutputStream stream(&manager_, params_);

        EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
            .WillOnce(Return(&stream));
        EXPECT_CALL(stream, Open())
            .WillOnce(Return(true));

        AudioOutputProxy* proxy = dispatcher->CreateStreamProxy();
        EXPECT_TRUE(proxy->Open());

        WaitForCloseTimer(&stream);
        proxy->Close();
        EXPECT_FALSE(stream.stop_called());
        EXPECT_FALSE(stream.start_called());
    }

    void OneStream_TwoPlays(AudioOutputDispatcher* dispatcher)
    {
        MockAudioOutputStream stream(&manager_, params_);

        EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
            .WillOnce(Return(&stream));

        EXPECT_CALL(stream, Open())
            .WillOnce(Return(true));
        EXPECT_CALL(stream, SetVolume(_))
            .Times(2);

        AudioOutputProxy* proxy1 = dispatcher->CreateStreamProxy();
        EXPECT_TRUE(proxy1->Open());

        proxy1->Start(&callback_);
        OnStart();
        proxy1->Stop();

        // The stream should now be idle and get reused by |proxy2|.
        AudioOutputProxy* proxy2 = dispatcher->CreateStreamProxy();
        EXPECT_TRUE(proxy2->Open());
        proxy2->Start(&callback_);
        OnStart();
        proxy2->Stop();

        proxy1->Close();
        CloseAndWaitForCloseTimer(proxy2, &stream);
        EXPECT_TRUE(stream.stop_called());
        EXPECT_TRUE(stream.start_called());
    }

    void TwoStreams_BothPlaying(AudioOutputDispatcher* dispatcher)
    {
        MockAudioOutputStream stream1(&manager_, params_);
        MockAudioOutputStream stream2(&manager_, params_);

        EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
            .WillOnce(Return(&stream1))
            .WillOnce(Return(&stream2));

        EXPECT_CALL(stream1, Open())
            .WillOnce(Return(true));
        EXPECT_CALL(stream1, SetVolume(_))
            .Times(1);

        EXPECT_CALL(stream2, Open())
            .WillOnce(Return(true));
        EXPECT_CALL(stream2, SetVolume(_))
            .Times(1);

        AudioOutputProxy* proxy1 = dispatcher->CreateStreamProxy();
        AudioOutputProxy* proxy2 = dispatcher->CreateStreamProxy();
        EXPECT_TRUE(proxy1->Open());
        EXPECT_TRUE(proxy2->Open());

        proxy1->Start(&callback_);
        proxy2->Start(&callback_);
        OnStart();
        proxy1->Stop();
        CloseAndWaitForCloseTimer(proxy1, &stream1);

        proxy2->Stop();
        CloseAndWaitForCloseTimer(proxy2, &stream2);

        EXPECT_TRUE(stream1.stop_called());
        EXPECT_TRUE(stream1.start_called());
        EXPECT_TRUE(stream2.stop_called());
        EXPECT_TRUE(stream2.start_called());
    }

    void StartFailed(AudioOutputDispatcher* dispatcher)
    {
        MockAudioOutputStream stream(&manager_, params_);

        EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
            .WillOnce(Return(&stream));
        EXPECT_CALL(stream, Open())
            .WillOnce(Return(true));

        AudioOutputProxy* proxy = dispatcher->CreateStreamProxy();
        EXPECT_TRUE(proxy->Open());

        WaitForCloseTimer(&stream);

        // |stream| is closed at this point. Start() should reopen it again.
        EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
            .Times(2)
            .WillRepeatedly(Return(reinterpret_cast<AudioOutputStream*>(NULL)));

        EXPECT_CALL(callback_, OnError(_))
            .Times(2);

        proxy->Start(&callback_);

        // Double Start() in the error case should be allowed since it's possible a
        // callback may not have had time to process the OnError() in between.
        proxy->Stop();
        proxy->Start(&callback_);

        Mock::VerifyAndClear(&callback_);

        proxy->Close();
    }

    void DispatcherDestroyed_BeforeOpen(
        std::unique_ptr<AudioOutputDispatcher> dispatcher)
    {
        EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _)).Times(0);
        AudioOutputProxy* proxy = dispatcher->CreateStreamProxy();
        dispatcher.reset();
        EXPECT_FALSE(proxy->Open());
        proxy->Close();
    }

    void DispatcherDestroyed_BeforeStart(
        std::unique_ptr<AudioOutputDispatcher> dispatcher)
    {
        MockAudioOutputStream stream(&manager_, params_);
        EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
            .WillOnce(Return(&stream));
        EXPECT_CALL(stream, Open()).WillOnce(Return(true));
        EXPECT_CALL(stream, Close()).Times(1);
        AudioOutputProxy* proxy = dispatcher->CreateStreamProxy();
        EXPECT_TRUE(proxy->Open());

        EXPECT_CALL(callback_, OnError(_)).Times(1);
        dispatcher.reset();
        proxy->Start(&callback_);
        proxy->Stop();
        proxy->Close();
    }

    void DispatcherDestroyed_BeforeStop(
        std::unique_ptr<AudioOutputDispatcher> dispatcher)
    {
        MockAudioOutputStream stream(&manager_, params_);
        EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
            .WillOnce(Return(&stream));
        EXPECT_CALL(stream, Open()).WillOnce(Return(true));
        EXPECT_CALL(stream, Close()).Times(1);
        EXPECT_CALL(stream, SetVolume(_)).Times(1);

        AudioOutputProxy* proxy = dispatcher->CreateStreamProxy();
        EXPECT_TRUE(proxy->Open());
        proxy->Start(&callback_);
        dispatcher.reset();
        proxy->Stop();
        proxy->Close();
    }

    void DispatcherDestroyed_AfterStop(
        std::unique_ptr<AudioOutputDispatcher> dispatcher)
    {
        MockAudioOutputStream stream(&manager_, params_);
        EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
            .WillOnce(Return(&stream));
        EXPECT_CALL(stream, Open()).WillOnce(Return(true));
        EXPECT_CALL(stream, Close()).Times(1);
        EXPECT_CALL(stream, SetVolume(_)).Times(1);

        AudioOutputProxy* proxy = dispatcher->CreateStreamProxy();
        EXPECT_TRUE(proxy->Open());
        proxy->Start(&callback_);
        proxy->Stop();
        dispatcher.reset();
        proxy->Close();
    }

    base::MessageLoop message_loop_;
    std::unique_ptr<AudioOutputDispatcherImpl> dispatcher_impl_;
    MockAudioManager manager_;
    MockAudioSourceCallback callback_;
    AudioParameters params_;
};

class AudioOutputResamplerTest : public AudioOutputProxyTest {
public:
    void TearDown() override { AudioOutputProxyTest::TearDown(); }

    void InitDispatcher(base::TimeDelta close_delay) override
    {
        // Use a low sample rate and large buffer size when testing otherwise the
        // FakeAudioOutputStream will keep the message loop busy indefinitely; i.e.,
        // RunUntilIdle() will never terminate.
        resampler_params_ = AudioParameters(
            AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO,
            16000, 16, 1024);
        resampler_ = base::MakeUnique<AudioOutputResampler>(
            &manager(), params_, resampler_params_, std::string(), close_delay);
    }

    void OnStart() override
    {
        // Let Start() run for a bit.
        base::RunLoop run_loop;
        message_loop_.task_runner()->PostDelayedTask(
            FROM_HERE, run_loop.QuitClosure(),
            base::TimeDelta::FromMilliseconds(kStartRunTimeMs));
        run_loop.Run();
    }

protected:
    AudioParameters resampler_params_;
    std::unique_ptr<AudioOutputResampler> resampler_;
};

TEST_F(AudioOutputProxyTest, CreateAndClose)
{
    AudioOutputProxy* proxy = dispatcher_impl_->CreateStreamProxy();
    proxy->Close();
}

TEST_F(AudioOutputResamplerTest, CreateAndClose)
{
    AudioOutputProxy* proxy = resampler_->CreateStreamProxy();
    proxy->Close();
}

TEST_F(AudioOutputProxyTest, OpenAndClose)
{
    OpenAndClose(dispatcher_impl_.get());
}

TEST_F(AudioOutputResamplerTest, OpenAndClose)
{
    OpenAndClose(resampler_.get());
}

// Create a stream, and verify that it is closed after kTestCloseDelayMs.
// if it doesn't start playing.
TEST_F(AudioOutputProxyTest, CreateAndWait)
{
    CreateAndWait(dispatcher_impl_.get());
}

// Create a stream, and verify that it is closed after kTestCloseDelayMs.
// if it doesn't start playing.
TEST_F(AudioOutputResamplerTest, CreateAndWait)
{
    CreateAndWait(resampler_.get());
}

TEST_F(AudioOutputProxyTest, StartAndStop)
{
    StartAndStop(dispatcher_impl_.get());
}

TEST_F(AudioOutputResamplerTest, StartAndStop)
{
    StartAndStop(resampler_.get());
}

TEST_F(AudioOutputProxyTest, CloseAfterStop)
{
    CloseAfterStop(dispatcher_impl_.get());
}

TEST_F(AudioOutputResamplerTest, CloseAfterStop)
{
    CloseAfterStop(resampler_.get());
}

TEST_F(AudioOutputProxyTest, TwoStreams)
{
    TwoStreams(dispatcher_impl_.get());
}

TEST_F(AudioOutputResamplerTest, TwoStreams)
{
    TwoStreams(resampler_.get());
}

// Two streams: verify that second stream is allocated when the first
// starts playing.
TEST_F(AudioOutputProxyTest, OneStream_TwoPlays)
{
    OneStream_TwoPlays(dispatcher_impl_.get());
}

TEST_F(AudioOutputResamplerTest, OneStream_TwoPlays)
{
    OneStream_TwoPlays(resampler_.get());
}

// Two streams, both are playing. Dispatcher should not open a third stream.
TEST_F(AudioOutputProxyTest, TwoStreams_BothPlaying)
{
    TwoStreams_BothPlaying(dispatcher_impl_.get());
}

TEST_F(AudioOutputResamplerTest, TwoStreams_BothPlaying)
{
    TwoStreams_BothPlaying(resampler_.get());
}

TEST_F(AudioOutputProxyTest, OpenFailed)
{
    OpenFailed(dispatcher_impl_.get());
}

// Start() method failed.
TEST_F(AudioOutputProxyTest, StartFailed)
{
    StartFailed(dispatcher_impl_.get());
}

TEST_F(AudioOutputResamplerTest, StartFailed)
{
    StartFailed(resampler_.get());
}

TEST_F(AudioOutputProxyTest, DispatcherDestroyed_BeforeOpen)
{
    DispatcherDestroyed_BeforeOpen(std::move(dispatcher_impl_));
}

TEST_F(AudioOutputResamplerTest, DispatcherDestroyed_BeforeOpen)
{
    DispatcherDestroyed_BeforeOpen(std::move(resampler_));
}

TEST_F(AudioOutputProxyTest, DispatcherDestroyed_BeforeStart)
{
    DispatcherDestroyed_BeforeStart(std::move(dispatcher_impl_));
}

TEST_F(AudioOutputResamplerTest, DispatcherDestroyed_BeforeStart)
{
    DispatcherDestroyed_BeforeStart(std::move(resampler_));
}

TEST_F(AudioOutputProxyTest, DispatcherDestroyed_BeforeStop)
{
    DispatcherDestroyed_BeforeStop(std::move(dispatcher_impl_));
}

TEST_F(AudioOutputResamplerTest, DispatcherDestroyed_BeforeStop)
{
    DispatcherDestroyed_BeforeStop(std::move(resampler_));
}

TEST_F(AudioOutputProxyTest, DispatcherDestroyed_AfterStop)
{
    DispatcherDestroyed_AfterStop(std::move(dispatcher_impl_));
}

TEST_F(AudioOutputResamplerTest, DispatcherDestroyed_AfterStop)
{
    DispatcherDestroyed_AfterStop(std::move(resampler_));
}

// Simulate AudioOutputStream::Create() failure with a low latency stream and
// ensure AudioOutputResampler falls back to the high latency path.
TEST_F(AudioOutputResamplerTest, LowLatencyCreateFailedFallback)
{
    MockAudioOutputStream stream(&manager_, params_);
    EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
        .Times(2)
        .WillOnce(Return(static_cast<AudioOutputStream*>(NULL)))
        .WillRepeatedly(Return(&stream));
    EXPECT_CALL(stream, Open())
        .WillOnce(Return(true));

    AudioOutputProxy* proxy = resampler_->CreateStreamProxy();
    EXPECT_TRUE(proxy->Open());
    CloseAndWaitForCloseTimer(proxy, &stream);
}

// Simulate AudioOutputStream::Open() failure with a low latency stream and
// ensure AudioOutputResampler falls back to the high latency path.
TEST_F(AudioOutputResamplerTest, LowLatencyOpenFailedFallback)
{
    MockAudioOutputStream failed_stream(&manager_, params_);
    MockAudioOutputStream okay_stream(&manager_, params_);
    EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
        .Times(2)
        .WillOnce(Return(&failed_stream))
        .WillRepeatedly(Return(&okay_stream));
    EXPECT_CALL(failed_stream, Open())
        .WillOnce(Return(false));
    EXPECT_CALL(failed_stream, Close())
        .Times(1);
    EXPECT_CALL(okay_stream, Open())
        .WillOnce(Return(true));

    AudioOutputProxy* proxy = resampler_->CreateStreamProxy();
    EXPECT_TRUE(proxy->Open());
    CloseAndWaitForCloseTimer(proxy, &okay_stream);
}

// Simulate failures to open both the low latency and the fallback high latency
// stream and ensure AudioOutputResampler falls back to a fake stream.
TEST_F(AudioOutputResamplerTest, HighLatencyFallbackFailed)
{
    MockAudioOutputStream okay_stream(&manager_, params_);

// Only Windows has a high latency output driver that is not the same as the low
// latency path.
#if defined(OS_WIN)
    static const int kFallbackCount = 2;
#else
    static const int kFallbackCount = 1;
#endif
    EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
        .Times(kFallbackCount)
        .WillRepeatedly(Return(static_cast<AudioOutputStream*>(NULL)));

    // To prevent shared memory issues the sample rate and buffer size should
    // match the input stream parameters.
    EXPECT_CALL(manager(),
        MakeAudioOutputStream(
            AllOf(testing::Property(&AudioParameters::format,
                      AudioParameters::AUDIO_FAKE),
                testing::Property(&AudioParameters::sample_rate,
                    params_.sample_rate()),
                testing::Property(&AudioParameters::frames_per_buffer,
                    params_.frames_per_buffer())),
            _, _))
        .Times(1)
        .WillOnce(Return(&okay_stream));
    EXPECT_CALL(okay_stream, Open())
        .WillOnce(Return(true));

    AudioOutputProxy* proxy = resampler_->CreateStreamProxy();
    EXPECT_TRUE(proxy->Open());
    CloseAndWaitForCloseTimer(proxy, &okay_stream);
}

// Simulate failures to open both the low latency, the fallback high latency
// stream, and the fake audio output stream and ensure AudioOutputResampler
// terminates normally.
TEST_F(AudioOutputResamplerTest, AllFallbackFailed)
{
// Only Windows has a high latency output driver that is not the same as the low
// latency path.
#if defined(OS_WIN)
    static const int kFallbackCount = 3;
#else
    static const int kFallbackCount = 2;
#endif
    EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
        .Times(kFallbackCount)
        .WillRepeatedly(Return(static_cast<AudioOutputStream*>(NULL)));

    AudioOutputProxy* proxy = resampler_->CreateStreamProxy();
    EXPECT_FALSE(proxy->Open());
    proxy->Close();
}

// Simulate an eventual OpenStream() failure; i.e. successful OpenStream() calls
// eventually followed by one which fails; root cause of http://crbug.com/150619
TEST_F(AudioOutputResamplerTest, LowLatencyOpenEventuallyFails)
{
    MockAudioOutputStream stream1(&manager_, params_);
    MockAudioOutputStream stream2(&manager_, params_);

    // Setup the mock such that all three streams are successfully created.
    EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
        .WillOnce(Return(&stream1))
        .WillOnce(Return(&stream2))
        .WillRepeatedly(Return(static_cast<AudioOutputStream*>(NULL)));

    // Stream1 should be able to successfully open and start.
    EXPECT_CALL(stream1, Open())
        .WillOnce(Return(true));
    EXPECT_CALL(stream1, SetVolume(_))
        .Times(1);

    // Stream2 should also be able to successfully open and start.
    EXPECT_CALL(stream2, Open())
        .WillOnce(Return(true));
    EXPECT_CALL(stream2, SetVolume(_))
        .Times(1);

    // Open and start the first proxy and stream.
    AudioOutputProxy* proxy1 = resampler_->CreateStreamProxy();
    EXPECT_TRUE(proxy1->Open());
    proxy1->Start(&callback_);
    OnStart();

    // Open and start the second proxy and stream.
    AudioOutputProxy* proxy2 = resampler_->CreateStreamProxy();
    EXPECT_TRUE(proxy2->Open());
    proxy2->Start(&callback_);
    OnStart();

    // Attempt to open the third stream which should fail.
    AudioOutputProxy* proxy3 = resampler_->CreateStreamProxy();
    EXPECT_FALSE(proxy3->Open());
    proxy3->Close();

    // Perform the required Stop()/Close() shutdown dance for each proxy.  Under
    // the hood each proxy should correctly call CloseStream() if OpenStream()
    // succeeded or not.
    proxy2->Stop();
    CloseAndWaitForCloseTimer(proxy2, &stream2);

    proxy1->Stop();
    CloseAndWaitForCloseTimer(proxy1, &stream1);

    EXPECT_TRUE(stream1.stop_called());
    EXPECT_TRUE(stream1.start_called());
    EXPECT_TRUE(stream2.stop_called());
    EXPECT_TRUE(stream2.start_called());
}

// Simulate failures to open both the low latency and the fallback high latency
// stream and ensure AudioOutputResampler falls back to a fake stream.  Ensure
// that after the close delay elapses, opening another stream succeeds with a
// non-fake stream.
TEST_F(AudioOutputResamplerTest, FallbackRecovery)
{
    MockAudioOutputStream fake_stream(&manager_, params_);

    // Trigger the fallback mechanism until a fake output stream is created.
#if defined(OS_WIN)
    static const int kFallbackCount = 2;
#else
    static const int kFallbackCount = 1;
#endif
    EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
        .Times(kFallbackCount)
        .WillRepeatedly(Return(static_cast<AudioOutputStream*>(NULL)));
    EXPECT_CALL(manager(),
        MakeAudioOutputStream(
            AllOf(testing::Property(&AudioParameters::format,
                      AudioParameters::AUDIO_FAKE),
                testing::Property(&AudioParameters::sample_rate,
                    params_.sample_rate()),
                testing::Property(&AudioParameters::frames_per_buffer,
                    params_.frames_per_buffer())),
            _, _))
        .WillOnce(Return(&fake_stream));
    EXPECT_CALL(fake_stream, Open()).WillOnce(Return(true));
    AudioOutputProxy* proxy = resampler_->CreateStreamProxy();
    EXPECT_TRUE(proxy->Open());
    CloseAndWaitForCloseTimer(proxy, &fake_stream);

    // Once all proxies have been closed, AudioOutputResampler will start the
    // reinitialization timer and execute it after the close delay elapses.
    base::RunLoop run_loop;
    message_loop_.task_runner()->PostDelayedTask(
        FROM_HERE, run_loop.QuitClosure(),
        base::TimeDelta::FromMilliseconds(2 * kTestCloseDelayMs));
    run_loop.Run();

    // Verify a non-fake stream can be created.
    MockAudioOutputStream real_stream(&manager_, params_);
    EXPECT_CALL(manager(),
        MakeAudioOutputStream(
            testing::Property(&AudioParameters::format,
                testing::Ne(AudioParameters::AUDIO_FAKE)),
            _, _))
        .WillOnce(Return(&real_stream));

    // Stream1 should be able to successfully open and start.
    EXPECT_CALL(real_stream, Open()).WillOnce(Return(true));
    proxy = resampler_->CreateStreamProxy();
    EXPECT_TRUE(proxy->Open());
    CloseAndWaitForCloseTimer(proxy, &real_stream);
}

} // namespace media
